using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using System;
using System.Collections.Generic;

namespace IQIGame.Onigao.GamePlay
{
    public class UIN_HeroUpInfo : UIBaseNode
    {

        #region Template Generate,don't modify
        protected partial class UIB_UIN_HeroUpInfo
        {
            #region ObjectBinding Generate
            public IQIGame.Onigao.Framework.ExProgressBar expBarNextLv { protected set; get; }
            public IQIGame.Onigao.Framework.ExText textLvCurr { protected set; get; }
            public IQIGame.Onigao.Framework.ExText textLvNext { protected set; get; }
            public UnityEngine.GameObject costMoney { protected set; get; }
            public UnityEngine.GameObject needRoot { protected set; get; }
            public UnityEngine.GameObject maxLv { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btnMax { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btnNext { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btnAwake { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btnUpdate { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btnClear { protected set; get; }
            public UnityEngine.GameObject costItems { protected set; get; }
            public IQIGame.Onigao.Framework.ExImage iconHero { protected set; get; }
            public IQIGame.Onigao.Framework.ExText textLv { protected set; get; }
            public IQIGame.Onigao.Framework.ExProgressBar expBarMiddle { protected set; get; }
            public virtual void InitBinding(ObjectBinding __binding)
            {
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExProgressBar>("expBarNextLv", out var __tbv0);
                this.expBarNextLv = __tbv0;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExText>("textLvCurr", out var __tbv1);
                this.textLvCurr = __tbv1;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExText>("textLvNext", out var __tbv2);
                this.textLvNext = __tbv2;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("costMoney", out var __tbv3);
                this.costMoney = __tbv3;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("needRoot", out var __tbv4);
                this.needRoot = __tbv4;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("maxLv", out var __tbv5);
                this.maxLv = __tbv5;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btnMax", out var __tbv6);
                this.btnMax = __tbv6;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btnNext", out var __tbv7);
                this.btnNext = __tbv7;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btnAwake", out var __tbv8);
                this.btnAwake = __tbv8;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btnUpdate", out var __tbv9);
                this.btnUpdate = __tbv9;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btnClear", out var __tbv10);
                this.btnClear = __tbv10;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("costItems", out var __tbv11);
                this.costItems = __tbv11;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExImage>("iconHero", out var __tbv12);
                this.iconHero = __tbv12;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExText>("textLv", out var __tbv13);
                this.textLv = __tbv13;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExProgressBar>("expBarMiddle", out var __tbv14);
                this.expBarMiddle = __tbv14;
            }
            #endregion ObjectBinding Generate 
        }
        #endregion Template Generate,don't modify

        #region fields & properties
        UIN_CostMoney costMoney = null;
        IrregularListAdapter<UIN_HeroUpItem> costItems = null;

        int cntItemCost = 0;
        HeroData heroData;
        bool isLvChg = false;
        int lvInit = 0;
        int lvCurr = 0;
        int lvMax = 0;
        int expInit = 0;
        int expCurr = 0;
        int expNext = 0;
        int upLvCostMoney = 0;
        int addAllExp = 0;
        Dictionary<int, int> upLvCost = new();

        protected UIB_UIN_HeroUpInfo ui { get; set; }
        #endregion

        protected override void BeforeInit()
        {
            ui = new UIB_UIN_HeroUpInfo();
            ui.InitBinding(this.csObjBind);
        }

        protected override void OnInit()
        {
            this.costMoney = this.InitChildNode<UIN_CostMoney>(this.ui.costMoney);
            this.costItems = this.InitIrregularList<UIN_HeroUpItem>(this.ui.costItems, this.OnCostItemChanged);

            this.ui.btnAwake.onClick.AddListener(this.OnClickBtnAwake);
            this.ui.btnClear.onClick.AddListener(this.OnClickBtnClear);
            this.ui.btnMax.onClick.AddListener(this.OnClickBtnMaxLv);
            this.ui.btnNext.onClick.AddListener(this.OnClickBtnNextLv);
            this.ui.btnUpdate.onClick.AddListener(this.OnClickBtnUpdate);

            this.cntItemCost = APIUIHeroInfo.UpLvCostItems.Count / APIUIHeroInfo.OneUpHeroLvCostCount;
        }

        protected override void OnShow()
        {

        }

        protected override void OnHide()
        {
            this.heroData = null;
        }

        protected override void OnDispose()
        {
            this.heroData = null;
            this.costMoney = null;
            this.costItems = null;
        }

        public void Show(HeroData heroData)
        {
            this.heroData = heroData;
            if (heroData == null)
                return;

            this.Show();
            this.ViewShow();
        }

        #region 单击事件
        void OnClickBtnAwake()
        {
            UIN_HeroUpdate parent = this.parentNode as UIN_HeroUpdate;
            if (parent == null)
                return;
            parent.ViewUp(true);
        }

        void OnClickBtnClear()
        {
            this.ViewOriginal();
        }

        void OnClickBtnMaxLv()
        {
            if (this.lvCurr == this.lvMax)
                return;
            int diffLv = this.lvMax - this.lvCurr;
            int lv = diffLv > 2 ? this.lvCurr + diffLv / 2 : this.lvCurr + 1;
            int lvLast = lv;
            bool isChged = false;
            while (this.CalcPreview(ref lv))
            {
                isChged = true;
                if (lv < lvLast || lv == this.lvMax)
                    break;
                if (lv > this.lvMax)
                {
                    lv = this.lvMax;
                    this.CalcPreview(ref lv);
                    break;
                }
                diffLv = this.lvMax - lv;
                lv = diffLv > 2 ? lv + diffLv / 2 : lv + 1;
                lvLast = lv;
            }

            if (isChged)
            {
                this.lvCurr = lv;
                this.isLvChg = this.lvCurr != this.lvInit;
                this.ViewUpPreview();
            }
        }

        void OnClickBtnNextLv()
        {
            int lv = Math.Min(this.lvCurr + 1, this.lvMax);
            if (this.lvCurr == lv)
                return;

            if (this.CalcPreview(ref lv))
            {
                this.lvCurr = lv;
                this.isLvChg = this.lvCurr != this.lvInit;
                this.ViewUpPreview();
            }
        }

        void OnClickBtnUpdate()
        {
            if (this.upLvCost == null || this.upLvCost.Count == 0)
            {
                NoticeModule.ShowNoticeNoCall(APIUIConst.NoticeCid_UpPreNeed);
                return;
            }

            if (!this.costMoney.CheckIsEnough())
            {
                return;
            }

            this.SendUpLv().Forget();
        }

        async UniTaskVoid SendUpLv()
        {
            var result = await HeroModule.NetCore.CS_upLevel(this.heroData.cid, upLvCost);
            if (result.IsSuccess)
            {
                List<int> list = ListPool<int>.Get();
                foreach (int cid in APIUIHeroInfo.UpLevelShowAttributeIds)
                {
                    list.Add(this.heroData.GetAttr(cid));
                }
                HeroModule.Instance.UpHeroByPOD(result.heroPOD);

                if (this.lvInit < this.heroData.level)
                {
                    UD_HeroUpLv udUP = UD_HeroUpLv.NewFromPool().SyncUpLeve(this.heroData, this.lvInit, list);
                    this.OpenUI<UIHeroUpdatePop>(udUP);
                }

                bool isMaxLv = this.lvMax <= this.lvCurr;
                if (isMaxLv)
                    (this.parentNode as UIN_HeroUpdate)?.ViewDefault();
                else
                    this.ViewShow();
            }
        }
        #endregion

        void ViewShow()
        {
            this.ViewOriginal();

            bool isMaxLv = this.lvMax <= this.lvCurr;
            this.ui.maxLv.SetActive(isMaxLv);
            this.ui.needRoot.SetActive(!isMaxLv);
        }

        void ViewOriginal()
        {
            if (this.heroData == null)
                return;
            this.lvInit = this.heroData.level;
            this.lvCurr = this.lvInit;
            this.lvMax = this.heroData.maxLv;
            this.expInit = this.heroData.exp;
            this.expCurr = this.expInit;
            this.expNext = this.heroData.cfgLv.NextEXP;
            this.isLvChg = false;
            this.upLvCostMoney = 0;
            this.addAllExp = 0;
            this.upLvCost.Clear();

            this.ViewExpBar(this.expCurr, this.expNext);
            this.ViewLv(this.lvCurr);
            this.ShowHeroIcon();
            this.ui.btnAwake.text = APIUIHeroInfo.GetAwakenLevelTitle(this.heroData.awakenLevel);

            this.costMoney.Hide();
            this.costItems.RefillCells(this.cntItemCost);
        }

        void ViewExpBar(int expCurr, int expNext)
        {
            bool isMaxLv = this.lvCurr >= this.lvMax;
            bool isMaxPreview = isMaxLv && this.lvInit != this.lvCurr;
            int diffExp = expNext - expCurr;
            ExText csText = this.ui.expBarNextLv.textValue;
            if (csText != null)
            {
                //csText.indexSwitchColor = this.isLvChg ? 1 : (isMaxLv ? 2 : 0);
                csText.text = APIUIHeroInfo.GetNextExpStr(diffExp, isMaxLv);
            }

            int expBar = isMaxPreview ? 0 : this.expInit;
            int expPre = isMaxPreview ? 0 : expCurr;
            SetExpExProgressBar(this.ui.expBarNextLv, expBar, expNext, expPre);
            SetExpExProgressBar(this.ui.expBarMiddle, expBar, expNext, expPre);
        }

        private void SetExpExProgressBar(ExProgressBar expBar, float value, float max, float expPre)
        {
            expBar.SetValue(value, max).SetPreviewValue(expPre);
            var pbar = expBar.progressBar;
            if (pbar != null)
                pbar.SetActive(!this.isLvChg);
        }

        void ViewLv(int lv)
        {
            bool isMaxLv = lvMax == lv;
            this.ui.textLvCurr.text = lv.ToString();
            //this.ui.textLvCurr.indexSwitchColor = isLvChg ? 1 : 0;
            this.ui.textLvNext.text = "/" + lvMax;

            this.ui.textLv.text = lv.ToString();
        }

        bool CalcPreview(ref int toLv)
        {
            this.addAllExp = 0;
            this.upLvCost.Clear();

            int needExp = 0;
            for (int i = this.lvInit; i < toLv; i++)
            {
                var cfg = TableCenter.heroLevel.Get(i);
                if (cfg != null)
                    needExp += cfg.NextEXP;
            }
            needExp -= this.expInit;
            int kCostStart = (this.cntItemCost - 1) * APIUIHeroInfo.OneUpHeroLvCostCount;

            if (needExp <= 0 || kCostStart < 0)
            {
                return false;
            }

            var list = APIUIHeroInfo.UpLvCostItems;
            int costCid = 0;
            int costExp = 0;
            int addAllExp = 0;
            int hasNum = 0;
            List<int> listCostStart = ListPool<int>.Get();
            while (needExp > 0 && kCostStart >= 0)
            {
                costCid = list[kCostStart];
                hasNum = ItemModule.Instance.GetNum(costCid);
                if (hasNum > 0)
                {
                    if (!listCostStart.Contains(kCostStart))
                        listCostStart.Add(kCostStart);

                    costExp = list[kCostStart + 1];
                    if (needExp > costExp)
                    {
                        bool isCost = this.upLvCost.TryGetValue(costCid, out var cntUse);
                        if (cntUse < hasNum)
                        {
                            addAllExp += costExp;
                            cntUse++;
                            if (isCost)
                                this.upLvCost[costCid] = cntUse;
                            else
                                this.upLvCost.Add(costCid, cntUse);
                            needExp -= costExp;
                            continue;
                        }
                    }
                }
                kCostStart -= APIUIHeroInfo.OneUpHeroLvCostCount;
            }

            int lastHasCostStart = -1;
            if (listCostStart.Count > 0)
            {
                lastHasCostStart = listCostStart[listCostStart.Count - 1];
                listCostStart.Remove(lastHasCostStart);
            }

            while (needExp > 0 && lastHasCostStart >= 0)
            {
                costCid = list[lastHasCostStart];
                hasNum = ItemModule.Instance.GetNum(costCid);
                if (hasNum > 0)
                {
                    bool isCost = this.upLvCost.TryGetValue(costCid, out var cntUse);
                    if (cntUse < hasNum)
                    {
                        costExp = list[lastHasCostStart + 1];
                        addAllExp += costExp;
                        cntUse++;
                        if (isCost)
                            this.upLvCost[costCid] = cntUse;
                        else
                            this.upLvCost.Add(costCid, cntUse);
                        needExp -= costExp;
                    }
                    else if (listCostStart.Count > 0)
                    {
                        lastHasCostStart = listCostStart[listCostStart.Count - 1];
                        listCostStart.Remove(lastHasCostStart);
                    }
                    else
                    {
                        lastHasCostStart = -1;
                    }
                }
            }

            ListPool<int>.Put(listCostStart);

            if (needExp <= 0)
            {
                this.expCurr = needExp * -1;
                var toCfg = TableCenter.heroLevel.Get(toLv);
                this.expNext = toCfg?.NextEXP ?? 0;
                while (this.expNext > 0 && this.expCurr >= this.expNext && toLv <= this.lvMax)
                {
                    int lv = toLv + 1;
                    if (lv > this.lvMax)
                        break;

                    toLv = lv;
                    this.expCurr -= this.expNext;
                    var cfg = TableCenter.heroLevel.Get(toLv);
                    if (cfg != null)
                        this.expNext = cfg.NextEXP;
                }
            }
            else
            {
                int allExp = this.expInit + addAllExp;
                int lvCalc = this.lvInit;
                do
                {
                    var cfg = TableCenter.heroLevel.Get(lvCalc);
                    this.expNext = cfg?.NextEXP ?? 0;
                    if (allExp >= this.expNext)
                    {
                        allExp -= this.expNext;
                        lvCalc++;
                    }
                }
                while (this.expNext > 0 && allExp >= this.expNext && lvCalc != toLv);

                toLv = lvCalc;
                this.expCurr = allExp;
            }
            this.addAllExp = addAllExp > 0 ? addAllExp : 0;
            return this.upLvCost.Count > 0;
        }

        void CalcPreviewCostMoney()
        {
            this.upLvCostMoney = 0;
            if (this.addAllExp > 0)
            {
                var cost = APIUIEquip.UpLvCostMoney;
                this.upLvCostMoney = cost[1] * this.addAllExp;
            }
        }

        void ViewUpPreview()
        {
            this.costItems.RefillCells(this.cntItemCost);
            this.CalcPreviewCostMoney();
            if (this.upLvCostMoney > 0)
                this.costMoney.Show(APIUIHeroInfo.UpLvCostMoney[0], this.upLvCostMoney);
            else
                this.costMoney.Hide();

            this.ViewLv(this.lvCurr);
            this.ViewExpBar(this.expCurr, this.expNext);
        }

        void OnCostItemChanged(UIN_HeroUpItem item, int index)
        {
            int indexCid = APIUIHeroInfo.OneUpHeroLvCostCount * index;
            int cid = APIUIHeroInfo.UpLvCostItems[indexCid];
            int numCost = 0;
            this.upLvCost.TryGetValue(cid, out numCost);
            item.Show(cid, numCost);
        }

        void ShowHeroIcon()
        {
            this.SetImageSprite(this.ui.iconHero, this.heroData.cfgSkin.HeroHeadImage_fullPath);
        }
    }
}
